<?php
/**
 **************************************************************************************************************************************
 *中通相关的公用方法
 *zhouzhixiang@kuaidihelp.com
 **************************************************************************************************************************************
 */
namespace soa\partner\zt;
use artisan\cache;
use artisan\http;
use artisan\db;
use artisan\format;
use model\express\PayOnDelivery;
use soa\partner\DeliveryCommon;
use soa\partner\partnerCommon;


class ztCommon
{
    private $access_info = null;

    /**封装了中通合作平台技术接口的通用方法      文档地址：http://testpartner.zto.cn/
     *@param array
     *$data = [
     *      'url'        => 'http://testpartner.zto.cn/client/interface.php',    //接口地址（非必填，请注意测试和正式地址）
     *      'ztoPartner' => 'test',                 //ztoPartner     （中天系统中的账号）
     *      'ztoPass'    => 'ZTO123'                //ztoPass        （中天系统中的密码）
     *      'style'      => 'json'                  //接口需求的参数类型
     *      'func'       => 'order.marke'           //调用中通接口的哪个方法？（中通接口方法字段）
     *      'data'       => [
     *              'sendcity'     =>  '上海上海市长宁区'
     *              'receivercity' =>  '北京北京市朝阳区'
     *          ]
     * ];
     *
     *@return array
     */
    public function ztPartner($data){
        if(empty($data)  || !is_array($data)){
            raise(1009,'参数错误!');
        }
//logDebug($data,'ztPartner_data');
        //判断data中的数据是否合法开始
        $url        = empty($data['url']) ? 'http://testpartner.zto.cn/client/interface.php' : $data['url'] ;
        $ztoPartner = empty($data['ztoPartner']) ? raise(1009,'参数错误!') : $data['ztoPartner'] ;
        $ztoPass    = empty($data['ztoPass']) ? raise(1009,'参数错误!') : $data['ztoPass'] ;
        $style      = empty($data['style']) ? 'json' : $data['style'] ;
        $func       = empty($data['func']) ? raise(1009,'参数错误!') : $data['func'] ;
        $info       = empty($data['data']) ? raise(1009,'参数错误!') : $data['data'] ;
        //判断data中的数据是否合法结束

        //组织post到中通接口的参数开始
        $content=base64_encode(json_encode($info));
        $datetime = date('Y-m-d H:i:s',time());
        $verify=md5($ztoPartner.$datetime.$content.$ztoPass);

        //调用中通接口
        $result = http::post($url, array(
            "style" => $style,
            "func" => $func,
            "partner" =>$ztoPartner,
            "datetime" => $datetime,
            "content" => $content,
            "verify" => $verify
        ));
//        $post = array(
//            "style" => $style,
//            "func" => $func,
//            "partner" =>$ztoPartner,
//            "datetime" => $datetime,
//            "content" => $content,
//            "verify" => $verify
//        );
//logDebug($post,'ztPartner_post');
//logDebug($result,'ztPartner_result');
        //判断中通接口返回值，并return
        if(empty($result)){//中通接口无返回结果
            return '请求超时';
        }else{//有返回结果
            $result = json_decode($result, true);//解析返回的结果
            if(json_last_error() == 0){
                return $result;//返回
            }else{
                return '数据解析失败';
            }
        }
    }



    /**封装了中通快递开放平台系列接口的通用方法
     *@param array
     * $data = [
     *      'url'        => 'http://58.40.16.125:9001/zto/api_utf8/partnerOrderService',  //接口地址（不同业务接口地址不同）
     *      'company_id' => 'Kuaibao',                                                    //company_id(非必填)
     *      'partnerId'  => 'fde099cd2cf60738d765c48afb252a58',                           //parternId（非必填）
     *      'msg_type'   => 'queryInfo',                                                  //接口类型
     *      'data'       =>                                                               //需要传递的数据
     * ];
     *
     *@return array
     */
    public function ztOpenApi($data){
        if(empty($data) || !is_array($data)){
            raise(1009,'参数错误！');
        }
        //判断data中的数据是否合法开始
        $url        = empty($data['url']) ? raise(1009,'参数错误！') : $data['url'] ;
        $company_id = empty($data['company_id']) ? '' : $data['company_id'] ;//部分接口此参数可以不填(Kuaibao)
        $partnerId  = empty($data['partnerId']) ? '' : $data['partnerId'];//部分接口此参数可以不填(fde099cd2cf60738d765c48afb252a58)
        $msg_type   = empty($data['msg_type']) ? raise(1009,'参数错误！') : $data['msg_type'] ;
        $info       = $data['data'];//empty($data['data']) ? raise(1009,'参数错误！') : $data['data'] ;
        //判断data中的数据是否合法结束



        //组织post到中通接口的数据开始
        $json = json_encode($info);
        $post = [
            //'data_digest' => (base64_encode(md5($json.$partnerId,true))),//消息签名data_digest=base64(md5(data字段+partnerID))
            'msg_type'    => $msg_type,//消息类型(queryInfo  查询订单信息)
            //'company_id'  => $company_id,//合作商ID
            'data'        => $json       //消息内容
        ];
        if($partnerId){
            $post['data_digest'] = base64_encode(md5($json.$partnerId,true));//消息签名data_digest=base64(md5(data字段+partnerID))
        }
        if($company_id){
            $post['company_id']  = $company_id;//合作商ID
        }
        //组织post到中通接口的数据结束


        /**调用掌中通接口需要传递以下参数**/
        if(!empty($data['other']) && is_array($data['other'])){
            foreach($data['other'] as $k=>$v){
                $post[$k] = $v;
            }
        }
        /**调用掌中通接口需要传递以上参数**/


        //http_post请求中通接口开始
        $result = http::post($url, $post, array('HTTPHEADER' => array(
            'Content-type:application/x-www-form-urlencoded; charset=GBK'
        )));

        if(!empty($data['other'])){
            logDebug($post,'zztLogDebug');
        }

        //http_post请求中通接口结束
        $ret = json_decode($result,true);
        return $ret;
    }


    /**
     *封装了中通G000系列接口（传XML格式）的通用方法（中通速递手持终端数据接口  公司共享里有文档）
     *@param string  $cm_phone         //快递员手机号
     *$data['ver']      //XML数据中的版本号；可以不传，有默认值    （例如：4.0）
     *$data['data_ver'] //post数据中的版本号；可以不传，有默认值   （例如：4.0）
     *$data['optype']   //中通接口号   （例如：G010）
     *$data['data']     //需要传递的数据有两种类型 1:<a>111</a><b>222</b><c>333</c>...  2:<a>A=1*B=2*C+3...</a>
     *$data['url]       //接口地址；可以不传，有默认值
     *@param string $is_length        //中通G000系列接口参数大致有3类，有的不需要参数，也不需要长度，此时此处可以传0，其它情况此字段无需传值，默认为 1
     *
     *@return array
     */
    public function ztXMLInterface($cm_phone, $data, $is_length = '1', $other = []){

        if(empty($cm_phone) || empty($data) || !is_array($data)){
            raise(1009,'参数错误！');
        }

        $ver      = empty($data['ver']) ? '4.0' : $data['ver'] ;                        //XML版本号
        $data_ver = empty($data['data_ver']) ? '4.0' : $data['data_ver'] ;              //post版本号
        $url      = empty($data['url']) ? 'http://pda.zto.cn/pda.zto' : $data['url'] ;  //接口地址
        if($is_length){
            $info     = empty($data['data']) ? raise(1009,'参数错误！') : trim($data['data']) ;    //XML中P标签内的数据
        }else{
            $info = '';
        }
        $optype   = empty($data['optype']) ? raise(1009,'参数错误！') : $data['optype'] ;//接口号  G001

        if(empty($this->access_info)){
            $result = partnerCommon::getAccessInfo(['username'=>$cm_phone,'brand'=>'zt']);
            if($result){
                $this->access_info = $result;
            }
        }
        $scan_access_info = $this->access_info;
        //通过快递员手机号获取快递员巴枪数据开始
//        $scan_access_info = \artisan\db::connect('kd_pda')->table('tbl_scan_courier_zt')->select('emp_no,emp_name,emp_pwd,cm_phone,branch_no,branch_name,key,imei,index_shop_id,home_shop_id')->getOne(array('cm_phone' => $cm_phone, 'switch' => 'on', 'state' => 'approve'));
//logdebug($scan_access_info,'zt_send_datas_scan_access_info');
        if(empty($scan_access_info)){
            return [];
//            raise(4016, "快递员信息有误");
        }

        $openId = \artisan\cache::get($scan_access_info['emp_no'] ."openid:new");   //获取openid
        if(empty($openId)){//若无openid重新登录
///*
            $ztObj = new \soa\parter\zt\pda();
            $openId = $ztObj->login($scan_access_info);
//*/
/*
            if(rand(0, 1000) <= 50){//走新方法
                $timeStart = time();
                $openId = $this->login($scan_access_info);
                logDebug([
                    'timeOut'        => time()-$timeStart,
                    'openId'         => $openId,
                    'scanAccessInfo' => $scan_access_info,
                ], 'openIdBak');
            }else{//走老的获取openId方法
                $ztObj = new \soa\parter\zt\pda();
                $openId = $ztObj->login($scan_access_info);
            }
*/

//                $openId = \artisan\cache::get($scan_access_info['emp_no'] ."openid:new");
            if(empty($openId)){
                return [];
//                    raise(4016, "系统异常，请稍后重试");
            }
//                $openId = $ztObj->login($scan_access_info);
//                var_dump($scan_access_info);
//                var_dump($openId);
//                if(!preg_match('/[0-9A-Za-z]{32}/', $openId)){//openId为32为数字大小写字母组合
//                    raise(4016,$openId);
//                }
        }

        $retry = 0;
        while(1){
            //通过快递员手机号获取快递员巴枪数据结束
            //组织XML数据开始
            $expRule = '/ *\<\/?\w\>(\r\n)?/';//去除标签之间的空格，换行
            $str = preg_replace($expRule,'',$info);//将传入的<a>111</a><b>222</b>转换为111222
            $plength = mb_strlen($str,'UTF-8');//计算p标签长度
            $xml = '<?xml version="1.0" encoding="utf-8" ?>
<root>
<h>
  <ver>'.$ver.'</ver>
  <optype>'.$optype.'</optype>
  <manufacturer>KUAIBAOAPP</manufacturer>
  <u_company>'.$scan_access_info["branch_no"].'</u_company>
<u_code>';

            if($optype != 'G003')
                $xml .= $scan_access_info["emp_no"];

            $xml .= '</u_code>
  <sys_sn>'.$scan_access_info["imei"].'</sys_sn>
  <key>'.$scan_access_info["key"].'</key>';

            if($optype != 'G003')
                $xml .= '<openid>'.$openId.'</openid>';

            $xml .= '</h>
 <d>';
            if($is_length){
                $xml .= '<h>'.$plength.'</h>
  <p>'.$info.'</p>
 </d>
</root>';
            }else{
                $xml .='</d>
</root>';
            }
            //组织XML数据结束
            //发送到中通接口
            (mb_internal_encoding('UTF-8'));
            $post = array(
                'data_xml'  => mb_convert_encoding($xml, 'GBK'),//XML字符串转为GBK
                'data_sign' => ( base64_encode(md5($xml.'123456', true)) ),//base64(md5(XML拼接123456))
                'data_user' => 'KUAIBAOAPP',
                'data_ver' => $data_ver    //版本号
            );
            $startTime = time();

            $retXml = http::post($url, $post, array('HTTPHEADER' => array(
                'Content-type:multipart/form-data; charset=utf-8'
            )));

            $ret = format::xml2array($retXml);

            /**监测线上G036接口返回情况！**/
            if($optype == 'G036'){//录单推过去的数据和返回的数据入库做记录
//                logDebug($info, 'zt_G036Info', $scan_access_info["emp_no"]);
                /*
                //$info格式如下
                <a>CT=ZTO*VE=5.5.0*DE=0*VT=kuaibao*T=96*S=535032365221*HD=0*HN=*FS=20170619012232*PS=28036.059*FP=510000*FC=510100*FT=510104*FR=张静*FN=13855456666*FA=四川省成都市锦江区*FD=大观里靠近稻香苑*RP=510000*RC=510100*RT=510107*RR=先生*RN=15208476857*RA=四川省成都市武侯区*RD=二环路西一段双楠所有阳光4栋二单元12楼28号*FK=现付*WG=0*DS=0*FI=9570*CR=32907*CI=9570*WT=软,2散*LR=28036.059*CS=20170619012232*BZ=来自快宝录单</a>
                */
//                logDebug($ret, 'zt_G036Ret', $scan_access_info["emp_no"]);
                //匹配运单号-----$waybillNoMatchs['waybillNo']['0']//改为通过$other传递过来
//                preg_match_all('/S=(?<waybillNo>.*?)\*HD/', $info, $waybillNoMatchs);

                $sendData = [
                    'order_number'=> $other['orderNumber'],//订单号
                    'waybill'     => $other['waybillNo'],//运单号
                    'brand'       => 'zt',
                    'result'      => is_string($retXml) ? $retXml : json_encode($retXml),
                    'send_data'   => is_string($info) ? $info : json_encode($info),
                    'time_out'    => time() - $startTime,
                    'send_time'   => date('Y-m-d H:i:s'),
                ];
                $r = db::connect('orders')->table('tbl_order_send_record')->insert($sendData);
                if(!$r){
                    logDebug([
                        'orderNumber' => $other['orderNumber'],//订单号
                        'waybillNo'   => $other['waybillNo'],//运单号
                        'timeOut'     => time() - $startTime,
                        'brand'       => 'zt',
                        'result'      => is_string($retXml) ? $retXml : json_encode($retXml),
                        'getData'     => is_string($info) ? $info : json_encode($info),
                    ], 'ztSendOrderInsertErrorLog');
                }
            }
            if($optype == 'G003'){
                logDebug($xml, 'zt_G003Info');
                logDebug($ret, 'zt_G003Ret');
            }

            if(isset($ret['error']['reason']) && stripos($ret['error']['reason'], 'OPENID验证失败') !== false) {//如果是openID过期造成的；此处尝试重新登陆获取openID然后再调一次接口
                if($retry++ <1){
                    $ztObj = new \soa\parter\zt\pda();
                    $openId = $ztObj->login($scan_access_info);
                }else{
                    break;
                }
            }else{
                break;
            }
        }

        //返回结果
        return $ret;
    }


    /**
     * 调用G043接口重新登陆 获取openId
     * @param $accessInfo //直接传手机号，或者传partnerCommon::getAccessInfo()方法返回的数组
     * @param bool $noCache //是否使用缓存，true-优先使用缓存；如果false则直接调接口，不取缓存
     * @return bool
     * 成功返回openId；失败返回false
     */
    public function login($accessInfo, $noCache = true){//G043//G031登陆接口//$branch_no, $emp_no, $emp_pwd
        $ver      = '4.0';                        //XML版本号
        $data_ver = '4.0';              //post版本号
        $url      = 'http://pda.zto.cn/pda.zto';  //接口地址

        $scan_access_info = [];//初始化快递员巴枪数据
        if(
            is_array($accessInfo) &&
            isset($accessInfo['branch_no']) &&
            isset($accessInfo['emp_no']) &&
            isset($accessInfo['emp_pwd']) &&
            isset($accessInfo['imei']) &&
            isset($accessInfo['key']) &&
            isset($accessInfo['cm_phone']) &&
            $accessInfo['branch_no'] &&
            $accessInfo['emp_no'] &&
            $accessInfo['emp_pwd'] &&
            $accessInfo['imei'] &&
            $accessInfo['key'] &&
            $accessInfo['cm_phone']
        ){
            $scan_access_info = $accessInfo;
        }elseif(preg_match('/^1\d{10}$/', $accessInfo)){//通过快递员手机号获取快递员巴枪数据
            $scan_access_info = partnerCommon::getAccessInfo(['username'=>$accessInfo,'brand'=>'zt']);
        }

        if(empty($scan_access_info)){
            logDebug([
                'accessInfo'  => $accessInfo,
            ], 'G043LoginGetScanAccessInfoError', 'soa');
            return false;
        }

        $cacheKey = $scan_access_info['emp_no'].'openid:';//openId存入缓存的key
        if($noCache){//优先从缓存获取openId
            $openId = cache::connect('pda')->get($cacheKey);
            if($openId){
                return $openId;//如果缓存中取到openId则直接返回，否则继续向下调用接口
            }
        }

        //组织XML数据开始
        $info = '<a>'.$scan_access_info['branch_no'].'</a><b>'.$scan_access_info['emp_no'].'</b><c>'.$scan_access_info['emp_pwd'].'</c><d>Kuaibao</d><e>v1.0</e><f>v1.0</f>';
        $expRule = '/ *\<\/?\w\>(\r\n)?/';//去除标签之间的空格，换行
        $str = preg_replace($expRule,'',$info);//将传入的<a>111</a><b>222</b>转换为111222
        $plength = mb_strlen($str,'UTF-8');//计算p标签长度
        $xml = '<?xml version="1.0" encoding="utf-8" ?>
<root>
<h>
  <ver>'.$ver.'</ver>
  <optype>G043</optype>
  <manufacturer>KUAIBAOAPP</manufacturer>
  <u_company>'.$scan_access_info["branch_no"].'</u_company>
  <u_code>'.$scan_access_info["emp_no"].'</u_code>
  <sys_sn>'.$scan_access_info["imei"].'</sys_sn>
  <key>'.$scan_access_info["key"].'</key>
</h>
 <d>
<h>'.$plength.'</h>
  <p>'.$info.'</p>
 </d>
</root>';
        //组织XML数据结束
        //发送到中通接口
        (mb_internal_encoding('UTF-8'));
        $post = array(
            'data_xml'  => mb_convert_encoding($xml, 'GBK'),//XML字符串转为GBK
            'data_sign' => ( base64_encode(md5($xml.'123456', true)) ),//base64(md5(XML拼接123456))
            'data_user' => 'KUAIBAOAPP',
            'data_ver' => $data_ver    //版本号
        );

        $startTime = time();
        $retXML = http::post($url, $post, array('HTTPHEADER' => array(
            'Content-type:multipart/form-data; charset=utf-8'
        )));

        $ret = empty($retXML) ? '' : format::xml2array($retXML);


        //遇到key值不对应，再重试一次
        if(isset($ret['error']['reason']) && stripos($ret['error']['reason'], 'KEY值不对应') !== false){
            $key = $this->refreshKey($scan_access_info["imei"], $scan_access_info["key"], $scan_access_info['cm_phone']);   //刷新key
            if(!$key){
                return false;//刷新key失败
            }
            //调用G001刷新key
            //替换失效的KEY
            $xml = preg_replace('/<key>(.*)?<\/key>/', '<key>'.$key.'</key>',$xml);
            //发送到中通接口
            (mb_internal_encoding('UTF-8'));
            $post = array(
                'data_xml'  => mb_convert_encoding($xml, 'GBK'),//XML字符串转为GBK
                'data_sign' => ( base64_encode(md5($xml.'123456', true)) ),//base64(md5(XML拼接123456))
                'data_user' => 'KUAIBAOAPP',
                'data_ver' => $data_ver    //版本号
            );
            $startTime = time();
            $retXML = http::post($url, $post, array('HTTPHEADER' => array(
                'Content-type:multipart/form-data; charset=utf-8'
            )));
            $ret = empty($retXML) ? '' : format::xml2array($retXML);
        }

        //返回结果
        if(!empty($ret) && isset($ret['Success']['d']['p']['a']) && $ret['Success']['d']['p']['a'] == 0){//登陆成功
            //设置缓存 //暂时往base和pda两个缓存里设置，兼容老版本，等切过来之后可以去调base
            cache::set($cacheKey, $ret['Success']['d']['p']['b'], 25200);//存7个小时
            cache::connect('pda')->set($cacheKey, $ret['Success']['d']['p']['b'], 25200);//存7个小时
            return $ret['Success']['d']['p']['b'];//返回openId
        }else{
            logDebug([
                'cmPhone'  => $accessInfo['cm_phone'],
                'timeOut'  => time()-$startTime,
                'retXml'   => $retXML,
                'postData' => $post,
            ], 'G043LoginError', 'soa_'.$accessInfo['cm_phone'], true);
            return false;
        }
    }

    /**
     * 调用G001接口刷新key
     * @param $sys_sn
     * @param $key
     * @param $cmPhone
     * @return bool
     * 成功返回key；失败返回false或者raise抛错
     */
    public function refreshKey($sys_sn, $key, $cmPhone){//G001接口，解决中通pda接口返回 key值不对应问题
        $cacheKey = 'ztoKey_'.$cmPhone.'_'. $sys_sn;
        $ver      = '4.0';                        //XML版本号
        $data_ver = '4.0';              //post版本号
        $url      = 'http://pda.zto.cn/pda.zto';  //接口地址
        $xmlG001  = '<?xml version="1.0" encoding="utf-8" ?>
<root>
<h>
<ver>'.$ver.'</ver>
<optype>G001</optype>
<manufacturer>KUAIBAOAPP</manufacturer>
<u_company></u_company>
<u_code></u_code>
<sys_sn>'.$sys_sn.'</sys_sn>
<key>'.$key.'</key>
</h>
<d></d>
</root>';
        (mb_internal_encoding('UTF-8'));
        $post = array(
            'data_xml'  => mb_convert_encoding($xmlG001, 'GBK'),//XML字符串转为GBK
            'data_sign' => ( base64_encode(md5($xmlG001.'123456', true)) ),//base64(md5(XML拼接123456))
            'data_user' => 'KUAIBAOAPP',
            'data_ver' => $data_ver    //版本号
        );
        $startTime1 = time();
        $retXMLG001 = http::post($url, $post, array('HTTPHEADER' => array(
            'Content-type:multipart/form-data; charset=utf-8'
        )));
        $retG001 = empty($retXMLG001) ? '' : format::xml2array($retXMLG001);

        //返回结果
        if(isset($retG001['Success']['d']['p']['a'])){//获取key成功 //再调用一次G001
//var_dump($xmlG001);
            $xmlG001 = preg_replace(
                '/<key>(.*)?<\/key>/',
                '<key>'.$retG001['Success']['d']['p']['a'].'</key>',
                $xmlG001
            );
//var_dump($xmlG001);die;
            (mb_internal_encoding('UTF-8'));
            $post = array(
                'data_xml'  => mb_convert_encoding($xmlG001, 'GBK'),//XML字符串转为GBK
                'data_sign' => ( base64_encode(md5($xmlG001.'123456', true)) ),//base64(md5(XML拼接123456))
                'data_user' => 'KUAIBAOAPP',
                'data_ver' => $data_ver    //版本号
            );
            $startTime2 = time();
            $retXMLG001 = http::post($url, $post, array('HTTPHEADER' => array(
                'Content-type:multipart/form-data; charset=utf-8'
            )));
            $retG001 = empty($retXMLG001) ? '' : format::xml2array($retXMLG001);
//var_dump($retG001);
            $tblScanCourierZtDBObj = db::connect('kd_pda')->table('tbl_scan_courier_zt');

            if(isset($retG001['Success']['d']['p']['a'])){//成功获取key
                //入库
                $startTime3 = time();
                $updateHandle = $tblScanCourierZtDBObj->where(['imei'=>$sys_sn])->update(['key'=>$retG001['Success']['d']['p']['a']]);
                if(!$updateHandle){
                    logDebug([
                        'timeOut'  => time()-$startTime3,
                        'imei'     => $sys_sn,
                        'key'      => $retG001['Success']['d']['p']['a'],
                    ], 'G001refreshKeyUpdateError', 'soa');
                }
                //设置缓存 //暂时往base和pda两个缓存里设置，兼容老版本，等切过来之后可以去调base
                cache::set($cacheKey, $retG001['Success']['d']['p']['a']);
                cache::connect('pda')->set($cacheKey, $retG001['Success']['d']['p']['a']);

                return $retG001['Success']['d']['p']['a'];//返回key
            }elseif(isset($ret['error']['reason']) && $ret['error']['reason']){//失败
                if($ret['error']['reason'] == '非法登陆！'){
                    if($key != '0000000000'){
                        //已审核通过的设备不能登录，将key重置为0，并抛错提示用户到后台刷新
                        $tblScanCourierZtDBObj->where(['imei'=>$sys_sn])->update(['key'=>'0000000000']);
                        $errMsg = '设备密钥已失效，请到中天系统-PDA管理中刷新设备：'.$sys_sn.' 然后重试。若还有问题请联系APP客服';
                    }else{
                        //未审核通过的设备
                        $errMsg = '设备尚未审核通过，请到中天系统-PDA管理中激活或刷新设备：'.$sys_sn.' 然后重试。若还有问题请联系APP客服';
                    }
                }else{
                    $errMsg = $ret['error']['reason'].' 设备号：'.$sys_sn;
                }
            }
            logDebug([
                'timeOut'  => time()-$startTime2,
                'retXml'   => $retXMLG001,
                'postData' => $post,
            ], 'G001refreshKeySecondError', 'soa');
            if(isset($errMsg) && $errMsg){//如果有错误的话，抛错
                raise(9999, $errMsg);
            }
            return false;
        }
        logDebug([
            'timeOut'  => time()-$startTime1,
            'retXml'   => $retXMLG001,
            'postData' => $post,
        ], 'G001refreshKeyError', 'soa');
        return false;
    }


    /**
     * 中通根据单号查询手机号方法，支持批量查询
     *
     * 传入中通快递单号 @param array('768226466713','407359968808','765089638931');
     *
     * @return array(
     *          array(
     *              'billCode'=>'768226466713',
     *                 'senderAddr'=>'收件人详细地址',
     *                 'senderCity'=>'市',
     *                 'senderCounty'=>'县',
     *                 'senderProv'=>'省',
     *                 'senderMobile'=>'收件人电话'
     *              ),
     *         array(...),
     *         array(...)
     *      );
     *
     *注意！！！   此处虽然字段是sender开头，但是返回的是收件人的信息(中通接口那边反馈的)
     */
    public function getMobileAddressByWaybillNo($waybillNos,$cmPhone, $cmId, $empNo='', $type='address')
    {
        if(empty($waybillNos) || !is_array($waybillNos)){
            return array();
        }

        if(!in_array($type,array('address','phone'))){
            $type = 'address';  //type为phone不写入数据库
        }

        $isEdit = 0;
        $return = $insertDB = [];

        //1、从cache取数据
        if($cmPhone){
            $cacheKey = 'zt_address_info_' . $cmPhone . '-' . date('Y-m-d');
            $resultsFromCache = cache::connect('pda')->get($cacheKey);
            if(!empty($resultsFromCache) && is_array($resultsFromCache)){
                foreach($waybillNos as $waybillNo){
                    if(isset($resultsFromCache[$waybillNo])){
                        $info = $this->getRequest($type,$waybillNo,$resultsFromCache[$waybillNo]);
                        if($info){
                            $return[$waybillNo] = $info;
                        }
                    }
                }

                $waybillNos = array_merge(array_diff($waybillNos,array_keys($return)));
            }
        }

        $resultsFromCache = !empty($resultsFromCache) && is_array($resultsFromCache) ? $resultsFromCache : [];

        //2、从数据库取数据
        if(!empty($waybillNos)){
            $temp = http::post('http://express.core.kuaidihelp.com/v1/parter/zt/getMobileAddressByDB', array('waybillNo' => $waybillNos,'type'=>$type));
            $temp = json_decode($temp,true);
        }

        if(!empty($temp['data'])){
            foreach ($temp['data'] as $k => $v) {
                $waybillNo = $v['waybillNo'];
                $return[$waybillNo] = $this->getRequest($type,$waybillNo,$v['info']);

                if(!empty($v['info']['phone']) || !empty($v['info']['address'])){
                    $isEdit = 1;
                    $resultsFromCache[$waybillNo] = !empty($resultsFromCache[$waybillNo]) && is_array($resultsFromCache[$waybillNo]) ? $resultsFromCache[$waybillNo] : [];
                    $resultsFromCache[$waybillNo] = $this->getRequest('cache',$waybillNo,$resultsFromCache[$waybillNo],$v['info']);
                }
            }

            $waybillNos = array_merge(array_diff($waybillNos,array_keys($temp['data'])));
        }

        //3、从接口查数据
        if(!empty($waybillNos)){
            $c = 0;
            $chunk = array_chunk($waybillNos,100,false);//中通接口限制每次查询100个
            foreach($chunk as $arrNos){
                if('phone' == $type){
                    $ret = $this->getAddressOld($arrNos,$cmPhone);
                    if(isset($ret['Success'])){
                        if(isset($ret['Success']['d']['p']['a'])){
                            $ret[] = $ret['Success']['d']['p'];
                        }else{
                            $ret = $ret['Success']['d']['p'];
                        }

                        foreach($ret as $k => $v){
                            if(!empty($v['c'])){
                                $return[$v['a']]=[
                                    'waybillNo' => $v['a'],
                                    'info'      => [
                                        'phone' => $v['c'] ? substr($v['c'], stripos($v['c'], '1'), 11) : '',//从 数字1 开始截取11位,
                                    ]
                                ];

                                $isEdit = 1;
                                $resultsFromCache[$v['a']] = !empty($resultsFromCache[$v['a']])?$resultsFromCache[$v['a']]:[];
                                $resultsFromCache[$v['a']] = $this->getRequest('cache',$v['a'],$resultsFromCache[$v['a']],['name'=>$v['b'],'phone'=>$v['c']]);
                            }
                        }
                    }
                }else{
                    $ret = $this->getAddress($arrNos);
                    if($ret['status']){
                        foreach($ret['result']['list'] as $k => $v){
                            $phone = '';
                            if(!empty($v['senderMobile'])){
                                $start_position = stripos($v['senderMobile'],'1');
                                if($start_position !== false){
                                    $phone = substr($v['senderMobile'],$start_position,11);//从 数字1 开始截取11位
                                }
                            }
                            if(!empty($v['senderAddr'])) {
                                $return[$v['billCode']] = [
                                    'waybillNo' => $v['billCode'],
                                    'info' => [
                                        'name'     => '',
                                        'phone'    => $phone ?: '',
                                        'address'  => $v['senderAddr'] ?: '',
                                        'province' => $v['senderProv'] ?: '',
                                        'city'     => $v['senderCity'] ?: '',
                                        'area'     => $v['senderCounty'] ?: ''],
                                ];
                                $insertDB[$c] = [
                                    'waybillNo' => $v['billCode'],
                                    'info'      => [
                                        'cm_phone' => $cmPhone,
                                        'emp_no'   => $empNo,
                                        'name'     => '',
                                        'phone'    => $phone ?: '',
                                        'address'  => $v['senderAddr'] ?: '',
                                        'province' => $v['senderProv'] ?: '',
                                        'city'     => $v['senderCity'] ?: '',
                                        'area'     => $v['senderCounty'] ?: '',
                                        'date'     => date('Y-m-d')
                                    ]
                                ];
                                $c++;
                            }

                            if($v['senderMobile'] || $v['senderAddr']){
                                $info = [
                                    'name'     => '',
                                    'phone'    => $v['senderMobile'] ?: '',
                                    'address'  => $v['senderAddr'] ?: '',
                                    'province' => $v['senderProv'] ?: '',
                                    'city'     => $v['senderCity'] ?: '',
                                    'area'     => $v['senderCounty'] ?: '',
                                ];
                                $isEdit = 1;
                                $resultsFromCache[$v['billCode']] = !empty($resultsFromCache[$v['billCode']])?$resultsFromCache[$v['billCode']]:[];
                                $resultsFromCache[$v['billCode']] = $this->getRequest('cache',$v['billCode'],$resultsFromCache[$v['billCode']],$info);
                            }
                        }
                    }
                }

                unset($ret);
            }

            if('address' == $type && $insertDB){
                //通过中通接口查询到的数据存入数据库
                $chunkInsert = array_chunk($insertDB,90,false);
                foreach($chunkInsert as $v){
                    (new DeliveryCommon())->logMobileAddress('zt',$v);
//                    http::post('http://express.core.kuaidihelp.com/v1/parter/zt/getMobileAddressByWaybillNo', ['info' => $v]);
                }
            }
        }


        if(isset($cacheKey) && $isEdit && $resultsFromCache){
            cache::connect('pda')->set($cacheKey,$resultsFromCache);
            cache::connect('pda')->expireat($cacheKey, strtotime(date('Y-m-d') . ' + 1 day'));
        }

//logDebug($return,'zt_return');
        return $return;
    }

    /**
    *中通代收货款
    */
    public function collectionOrCashOnDelivery($cmPhone, $waybillNo){
        //验证数据合法性
        if(empty($cmPhone) || empty($waybillNo)){
            return false;
        }

        //组织需要调用的数据
        $data = [
            'ver'      => '4.0',
            'data_ver' => '4.0',
            'optype'   => 'G045',
            'data'     => '<a>'.$waybillNo.'</a>',
            'url'      => 'pda.zto.cn/pda.zto'//测试地址，正式可以不同传，有默认值
        ];
        //调通中通PDA接口
        $ret = $this->ztXMLInterface($cmPhone, $data, $is_length = '1');
        return $ret;
    }

    public function getCODList($cmPhone, $waybillNos)
    {
        if(empty($cmPhone) || empty($waybillNos) || !is_array($waybillNos)){
            return false;
        }

        $return = [];
        foreach($waybillNos as $waybillNo){
            $ret = $this->collectionOrCashOnDelivery($cmPhone,$waybillNo);

            //处理并返回接口信息
            if(isset($ret['Success']['d']['p'])){
                if(isset($ret['Success']['d']['p']['a']) && $ret['Success']['d']['p']['a']){
//logDebug($waybillNo.'有到付信息'.json_encode($ret,JSON_UNESCAPED_UNICODE),'deli_debug');
                    $ret = $ret['Success']['d']['p'];
                }else{
                    $ret = [];
                }
            }elseif(isset($ret['error'])){
                logDebug('获取中通代收货款失败！'.json_encode($ret,JSON_UNESCAPED_UNICODE),'COD_debug',$waybillNo);
            }

            if($ret){
                $return[$ret['a']] = [
                    'waybill_no'            =>  $ret['a'],
                    'collect_date'          =>  $ret['b']?:'',
                    'collect_branch'        =>  $ret['c']?:'',
                    'payment'                =>  $ret['d'],
                    'send_payment_fee'      =>  $ret['e']?:0,
                    'deliver_payment_fee'   =>  $ret['f']?:0,
                    'deliver_branch'        =>  ($ret['g'] && !is_array($ret['g']))?:'',
                    'deliver_date'          =>  ($ret['h'] && !is_array($ret['h']))?:'',
                    'state'                  =>  $ret['i']?:'',
                    'is_pay'                 =>  $ret['j'],
                ];
            }
        }

        return $return;
    }

    //地址老接口G018接口,电话信息比较全
    public function getAddressOld($arrNos,$cmPhone)
    {
        $data = ['optype'=>'G018','data'=>'<a>'.implode('|',$arrNos).'</a>'];
        $ret = $this->ztXMLInterface($cmPhone,$data,1);

        return $ret;
    }

    //地址新接口
    public function getAddress($arrNos)
    {
        if(empty($arrNos) && !is_array($arrNos)){
            return [];
        }

        $post = [
            'url'        => 'http://japi.zto.cn/zto/api_utf8/partnerOrderService',
            'company_id' => 'Kuaibao',
            'partnerId'  => 'fde099cd2cf60738d765c48afb252a58',
            'msg_type'   => 'queryInfo',
            'data'       => [
                'list'      => [],//单号 数组格式
                'partnerId' => 'fde099cd2cf60738d765c48afb252a58'      //partnerId//fde099cd2cf60738d765c48afb252a58//kuaibao
            ]
        ];
        $post['data']['list'] = $arrNos;
        $ret = $this->ztOpenApi($post);

        return $ret;
    }


//    public function getInfoByWillNo($mailno){
//        $content = [
//            "mailno" =>$mailno
//        ];
//        $content=base64_encode(json_encode($content));
//        $partner = $this->getZtoPartner();
//        $datetime = date('Y-m-d H:i:s',time());
//        $pwd = $this->getZtoPass();
//        $verify=md5($partner.$datetime.$content.$pwd);
//        //调用中通接口获取大头笔
//        $result = http::post($this->getZtoServiceUrl(), array(
//            "style" => 'json',
//            "func" => 'mail.status',//'mail.trace',
//            "partner" =>$partner,
//            "datetime" => $datetime,
//            "content" => $content,
//            "verify" => $verify,
//        ));
////logDebug(array(
//        "style" => 'json',
//    "func" => 'mail.trace',
//    "partner" =>$partner,
//    "datetime" => $datetime,
//    "content" => $content,
//    "verify" => $verify,
//),'post_array');
////logDebug($result,'getInfoByWillNo');
//        return $result;
//    }

    /**
     * 中通问题件信息
     *
     * @param $express_no
     * @param int $timeout
     * @return array
     */
    public function ztProblemInfo($express_no, $timeout = 10)
    {
        $return = array();
        if (empty($express_no)) {
            return [];
        }

        $url = 'http://problem.zt-express.com/ProblemInterface/SearchByCodes';
        $options = [
            'HTTPHEADER' => ["Content-Type: application/x-www-form-urlencoded;charset=GBK"],
            'TIMEOUT' => $timeout
        ];
        $post = array(
            'logistics_interface' => $express_no,
            'data_digest'         => (base64_encode(md5($express_no . 'ZTO_PROBLEM', true))),
            'msg_type'            => 'ZTO_PROBLEM'
        );

        $r = http::post($url, $post, $options);
        $result = json_decode($r, true);
        if ($result['Result'] == 'success') {
            $contents = json_decode($result['Content'], true);
            foreach ($contents as $val) {
                $return[$val['BILL_CODE']][] = $val;
            }
            return $return;
        } else {
            return [];
        }
    }

    protected $_timeout = 10;

    public function setTimeout($second = 5)
    {
        $second = intval($second);
        $this->_timeout = $second;
    }

    public function getRequest($type,$waybillNo,$data,$dataEx=[])
    {
        if('phone' == $type){
            if(empty($data['phone'])){
                return [];
            }
            return array(
                'waybillNo' => (string)$waybillNo,
                'info'      => array(
                    'phone' => !empty($data['phone']) && !in_array($data['phone'],['1**********'])? $data['phone'] : '',
                )
            );
        } elseif ('cache' == $type) {
            return array(
                'name'     => !empty($data['name']) ? $data['name'] : (!empty($dataEx['name']) ? $dataEx['name'] : ''),
                'phone'    => !empty($data['phone']) ? $data['phone'] : (!empty($dataEx['phone']) ? $dataEx['phone'] : ''),
                'province' => !empty($data['province']) ? $data['province'] : (!empty($dataEx['province']) ? $dataEx['province'] : ''),
                'city'     => !empty($data['city']) ? $data['city'] : (!empty($dataEx['city']) ? $dataEx['city'] : ''),
                'area'     => !empty($data['area']) ? $data['area'] : (!empty($dataEx['area']) ? $dataEx['area'] : ''),
                'address'  => !empty($data['address']) ? $data['address'] : (!empty($dataEx['address']) ? $dataEx['address'] : ''),
            );
        } else {
            if(empty($data['address'])){
                return [];
            }
            return array(
                'waybillNo' => (string)$waybillNo,
                'info'      => array(
                    'name'     => !empty($data['name']) ? $data['name'] : '',
                    'phone'    => !empty($data['phone']) && !in_array($data['phone'],['1**********'])? $data['phone'] : '',
                    'province' => !empty($data['province']) ? $data['province'] : '',
                    'city'     => !empty($data['city']) ? $data['city'] : '',
                    'area'     => !empty($data['area']) ? $data['area'] : '',
                    'address'  => !empty($data['address']) ? $data['address'] : '',
                )
            );
        }
    }
}